<?php

/**
 * imap 基础类
 */
class Imap extends Base
{
	/**
	 * 获取所有的邮箱文件夹 和 文件夹对应的邮件数量
	 */
	public function getFolders():array{
		$data=[]; // 初始化
		// 获取所有的邮件
		if($folders=$this->rcube_imap->list_folders_subscribed('','*','mail')){
			// 获取该邮件服务器文件夹分隔符
			$delimiter = $this->rcube_imap->get_hierarchy_delimiter();
			foreach ($folders as $key => $folder) {
				// 验证邮箱文件夹是否存在和邮箱文件夹的有效性
				$this->_deepBuildFolder($data,$folder,$delimiter); // 递归获取邮箱文件夹
			}
		}
		return $data;
	}

	/**
	 * 递归获取邮箱文件夹辅助类
	 * @param array $arrFolders 引用数组
	 * @param string $folder 邮箱文件夹
	 * @param string $delm 邮箱文件夹分隔符
	 * @param string $path 邮箱文件夹路径关系
	 * @return void
	 */
	private function _deepBuildFolder(array &$arrFolders,string $folder,string $delm = '/',string $path = ''){
		$prefix = '';
		if (!$path){
            $n_folder = $folder;
            $folder   = $this->rcube_imap->mod_folder($folder);
            if($n_folder != $folder){
                $prefix = substr($n_folder, 0, -strlen($folder));
            }
        }
        $pos = strpos($folder, $delm);
        if ($pos !== false){
            $subFolders    = substr($folder, $pos+1);
            $currentFolder = substr($folder, 0, $pos);
            if(!strlen($subFolders)){
                $virtual = false;
            }elseif(!isset($arrFolders[$currentFolder])){
                $virtual = true;
            }else{
                $virtual = $arrFolders[$currentFolder]['virtual'];
            }
        }else {
            $subFolders = false;
            $currentFolder = $folder;
            $virtual = false;
        }
        $path .= $prefix . $currentFolder;
        if (!isset($arrFolders[$currentFolder])){
            $folderData = $this->rcube_imap->folder_data($path);
            $name = rcube_charset::convert($currentFolder, 'UTF7-IMAP');// 解码邮箱文件夹
            $arrFolders[$currentFolder] = [
                'folder' => $path, // 用于业务参数传递
                'name' => $name,// 转码邮件文件夹 使用中文显示
                'chFolder' =>convertMailFolder($name), // 英文文件夹 翻译成中文显示
                'virtual' => $virtual,// 辅助参数             
                'count'=>isset($folderData['EXISTS']) ? $folderData['EXISTS'] : $this->rcube_imap->count($path), //对应邮件总数
                'unseenCount'=>isset($folderData['UNSEEN']) ? $folderData['UNSEEN'] : $this->rcube_imap->conn->countUnseen($path),//未读邮件的数量
                'childrens' => [], // 子文件夹的遍历 递归
            ];
        }else{
            $arrFolders[$currentFolder]['virtual'] = $virtual;
        }
        if(strlen($subFolders)){
            $this->_deepBuildFolder($arrFolders[$currentFolder]['childrens'], $subFolders, $delm, $path.$delm);
        }
	}
	
	/**
	 * 判断 文件夹扩展信息中的 attributes 一位数组中的索引值包含 "HasChildren" 的标识表示存在一维数组
	 * @return false:无子文件夹  true:标识存在子文件夹
	 */
	public function checkChildren(array $attributes,string $str='HasChildren'):bool{
	    $bool = false;
	    if($attributes && is_array($attributes)){
	        foreach ($attributes as $key => $attr) {
	        if(strpos($attr,$str) !== false){
	                $bool = true;
	            }
	        }
	    }
	    return $bool;
	}
	/**
	 * 设置字符编码
	 */
	public function setCharset(string $charset='utf-8'){
		$this->rcube_imap->set_charset($charset);
	}

	/**
	 * 创建邮箱文件夹
	 * @param $folder 创建的文件夹名称
	 * @param $parentFolder 父文件夹名称
	 * 1、$folder 文件夹名称 不能含有系统设定的分隔符
	 * 2、创建文件夹时不能含有分割符号
	 * 3、当创建的属于根目录时候 无需传递 $parrentFolde
	 * 4、部分邮箱不支持创建子目录
	 */
	public function createFolder(string $folder,string $parentFolder=''):array{
		if($folder == '') return ['errno'=>100,'msg'=>'创建的文件夹不能为空'];
		$delimiter = $this->rcube_imap->get_hierarchy_delimiter();// 邮件服务器文件夹分隔符
		if(strpos($folder,$delimiter) !== false){
			return ['errno'=>101,'msg'=>'创建的邮件文件夹中不能含有分隔符 '.$delimiter];
		}
		$folder = rcube_charset::convert($folder,'UTF-8',  'UTF7-IMAP'); // 编码
		if($parentFolder){
			// 暂不支持创建子目录
			// 创建子目录
			$bool = $this->rcube_imap->create_folder($parentFolder.$delimiter.$folder);
		}else{
			$bool = $this->rcube_imap->create_folder($folder);
		}
		if($bool){
			return ['errno'=>0,'msg'=>'success'];
		}
		return ['errno'=>102,'msg'=>'创建失败 Code : '.$this->rcube_imap->get_error_code().' ErrorInfo : '.$this->rcube_imap->get_error_str()];
	}

	/**
	 * 删除文件夹下所有的邮件 针对任何邮箱文件夹都有效
	 * 测试 foxmail 与腾讯企业邮箱有误差 ，foxmail的一个bug
	 * @param string $folder 邮箱文件夹
	 */
	public function clearFolderMail(string $folder){
		if($folder == '') return ['errno'=>103,'msg'=>'异常，未获取到邮箱文件夹名称'];
		if($this->rcube_imap->clear_folder($folder)){
			return ['errno'=>0,'msg'=>'success'];
		}
		return ['errno'=>105,'msg'=>
		'删除失败 Code : '.$this->rcube_imap->get_error_code().' ErrorInfo : '.$this->rcube_imap->get_error_str()];
	}

	/**
	 * 删除邮箱文件夹
	 * 1、系统文件夹不能删除
	 * 2、传递的邮箱文件夹必须是编码后的
	 * 3、文件夹下存在邮件不能删除
	 * 4、存在同名的邮箱文件夹时删除会失败 例如：163邮箱
	 */
	public function deleteFolder(string $folder=''):array{
		if($folder == '') return ['errno'=>103,'msg'=>'异常，未获取到邮箱文件夹名称'];
		if($this->rcube_imap->delete_folder($folder)){
			return ['errno'=>0,'msg'=>'success'];
		}
		return ['errno'=>104,'msg'=>
		'删除失败 Code : '.$this->rcube_imap->get_error_code().' ErrorInfo : '.$this->rcube_imap->get_error_str()];
	}

	/**
	 * 重命名文件夹 不支持系统文件夹
	 * @param $oldFolder 原文件夹名称 可带分隔符 重命名子文件夹
	 * @param $newFolder 新文件夹名称 不能带分隔符 重命名根文件夹
	 */

	public function renameFolder(string $folder,string $newFolder){
		if($folder =='' || $newFolder==''){
			throw new Exception("异常，未获取到邮箱文件夹名称", 106);			
		}
		$delimiter = $this->rcube_imap->get_hierarchy_delimiter();// 文件夹分隔符
		if(strpos($newFolder,$delimiter) !== false){
	        $msg = '重命名的文件夹不能含有字符 '.$delimiter;
	        throw new Exception($msg, 107);       
	    }
		$newFolder = rcube_charset::convert($newFolder, RCUBE_CHARSET, 'UTF7-IMAP');
		if(($pos=strrpos($folder,$delimiter)) !== false){
        	$newFolder = substr($folder,0,$pos+1) . $newFolder;
	    }
	    if($this->rcube_imap->rename_folder($folder,$newFolder)){
	        return ['errno'=>0,'msg'=>'success'];
	    }
		$msg = '失败 Code : '.$this->rcube_imap->get_error_code().' ErrorInfo : '.$this->rcube_imap->get_error_str();
		throw new Exception($msg, 108);		
	}

	/**
	 * 复制邮件
	 * @param mixed $uids 可以是数组 或按英文,分割的字符串
	 * @param string $from 源文件夹
	 * @param string $to 目标文件夹
	 */
	public function copyMail($uids, string $from , string $to){
		return $this->rcube_imap->copy_message($uids,  $to,  $from);
	}

	/**
	 * 移动邮件
	 * @param mixed $uids 可以是数组 或按英文,分割的字符串
	 * @param string $from 源文件夹
	 * @param string $to 目标文件夹
	 */
	public function moveMail($uids,string $from , string $to){
		return $this->rcube_imap->move_message( $uids, $to, $from);
	}	

	/**
	 * 将邮件标记为已删除 将邮件拉倒拉到已删除邮箱
	 * @param mixed $uids 可以是数组 或按英文,分割的字符串
	 * @param string $folder
	 */
	public function deleteMail($uids,string $folder){
		return $this->rcube_imap->delete_message($uids,$folder);
	}

	/**
	 * 获取总共的页码数
	 */
	private function getPageTotal(int $pagesize=10){
		return ceil(count($this->uids) / $pagesize) ?? 0;
	}

	/**
	 * 获取 邮箱文件夹用于捕获 邮件的uid 列表
	 * 该方法必须自带分页功能
	 * @return 获取 rcube_result_index对象 专门用于获取 uid 列表
	 */
	private function getRcubeUidResult(string $folder,string $sort='DESC'){
		$rcube_result_index = $this->rcube_imap->index($folder,null,$sort); // 得到uid 对象
		$this->uids = $rcube_result_index->get(); // 得到一维数组获取 uid 列表
		return $this->uids;
	}

	/**
	 * 自定义 uid 分页功能
	 * @param $folder 邮箱文件夹名称 内部传参使用
	 * @param $p 页码
	 * @param $pagesize 一页显示的数量
	 * @param $sort 排序机制
	 */
	private function getUidPage(string $folder,int $p,int $pagesize=10,string $sort='DESC'){
		$p = $p>=1 ? $p : 1;
		$pagesize<=0 && $pagesize=10;
		$start = ($p-1)*$pagesize;
		return array_slice($this->uids,$start,$pagesize);
	}

	/**
	 * 根据邮箱文件夹名称 获取它的邮件列表按分页显示
	 * @param $folder 邮箱文件夹名称
	 * @param $p 页码
	 * @param $pagesize 邮箱一页显示的邮件数量
	 * @param $sort 排序规则
	 */
	public function getMailList(string $folder,int $p=1,int $pagesize=10,string $sort='DESC'){
		$result=[];//初始化返回值 获取
		// 获取该邮件的所有的 uids
		$uids = $this->getRcubeUidResult($folder,$sort);  
		if(!$uids || !is_array($uids)){
			$this->outputException("未获取到该邮件列表信息",99); 
		}
		// 获取页码总数 从1 开始 该值为 int 由前端生成吧
		$pageTotalCount = $this->getPageTotal($pagesize);
		// 按照分页规则获取 uid 列表
		$pageUids = $this->getUidPage($folder,$p,$pagesize,$sort); // array1		
		// 获取邮件列表的 headers 信息 组成二维数组键数量与 $pageUids键相等
		$headers=$this->rcube_imap->fetch_headers($folder,$pageUids);
		foreach ($headers as $header) {
			$date = date('Y-m-d H:i',strtotime($header->internaldate));
			$rcube_mime = new rcube_mime($header->charset);// 实例化字符集
			// 判断是否已读和星标邮件
			$flags = $header->flags;
			$seen = isset($flags['SEEN']) ? 1 : 0; // 已读/未读的标识  == 1 已读  == 0 未读
			$flagged = isset($flags['FLAGGED']) ? 1 : 0;// ==1 表示星标邮件
			$result[] = [
				'id'=>$header->id,
				'uid'=>$header->uid, // 邮件的唯一标识
				'subject' =>$rcube_mime->decode_header($header->subject),
				'date'=>$date .'  ['. getWeek($date) .']', // 邮件的发送时间
				'from'=>$rcube_mime->decode_header($header->from), // 邮件的发送者 正则提取出
				'to'=>$rcube_mime->decode_header($header->to), // 接收者
				'cc'=>$rcube_mime->decode_header($header->cc),// 发件人的抄送
				// 密送者
				'replyto'=>$rcube_mime->decode_header($header->replyto),//邮件的回复地址
				'seen'=>$seen,
				'flagged'=>$flagged,
			];
		}
		return $result;
	}

	/**
	 * 根据 uid 获取邮件的主题信息 内容/附件
	 */
	public function getMailBody(string $folder,$uid){
		// 获取邮件消息对象
		$rcube_message = new rcube_message($uid,$folder,true);
		$rcube_mime = new rcube_mime();// 邮件编解码对象	
		$sender= $rcube_message->sender; // 返回数组 得到邮件的发送者和发送人
		$sendname = $sender['name'] ?? '';
		$from = $sender['mailto'] ?? '';
		$subject = $rcube_message->subject; // string 邮件标题

		// 邮件标头对象
		$rcube_message_header = $rcube_message->headers;
		// $subject = $rcube_mime->decode_header($rcube_message_header->subject);
		// $from = $rcube_message_header->from;
		$to = $rcube_message_header->to; // 邮件接收者
		$cc = $rcube_message_header->cc; // 邮件的抄送人
		$replyto = $rcube_message_header->replyto; // 邮件的回复地址
		$uid = $rcube_message_header->uid; // 邮件的唯一标识
		$date = date('Y-m-d H:i',$rcube_message_header->timestamp);
		$date .= '  ['. getWeek($date) .']'; // 邮件的接收时间
		// 获取邮件的主体内容
		if($rcube_message->has_html_part()){ // 确认邮件是否含有 html部分  返回boolean
		    $body = $rcube_message->first_html_part();# 获取邮件内容正文部分 含html
		}else{
		    $body = $rcube_message->first_text_part(); #返回消息中 纯文本部分 没有html
		}
		// 返回array 邮件中所有的附件部分组成数组 有几个附件就是几维数组 无附件则返回空数组
		$attachments = $rcube_message->attachments;
		$attachs=[];// 附件 二维数组
		// 存在附件并获取附件数量
		if($attachmentCount = count($attachments)){
			// 返回数组 把一封邮件切分成几份 part[rcube_message_part对象]
			$parts = $rcube_message->mime_parts(); // 返回邮件
			// 遍历获得 rcube_message_part 对象
			foreach ($parts as $k => $part) {
				$mime_id = $part->mime_id;
			    // $charset = $part->charset; // 得到字符集
			    // $disposition = $part->disposition;
			    // $rcube_message->set_safe(); // 设置is_safe var和会话数据
			    // $rcube_message->uu_decode($part);
			    if($rcube_message->is_attachment($part)){
			        $attachs[] = [
			        	'filename' => $part->filename,
			        	'mimetype' => $part->mimetype, // 文件的mime类型
			        	'size' => $part->size, // 大小 字节
			        	'suffix' => $part->ctype_secondary, // 文件后缀
			        	'encoding' => $part->encoding, // 编码类型
						'mimeId' => $part->mime_id,
						'icon'=>/*业务层完成域名拼接 */RCUBE_ROOT_PATH.'/attach/'.replaceAttchIcon($part->ctype_secondary), // 根据suffix 显示图片
			        ];
			    }
			}
		}
		return [
			'folder'=>$folder,
			'subject'=>$subject, // 邮件标题
			'sendname'=>$sendname, // 发件人使用的姓名
			'from'=>$from, // 发件人的邮箱
			'to'=>$to,
			'cc'=>$cc,
			'replyto'=>$replyto,
			'uid'=>$uid,
			'date'=>$date,
			'body'=>$body,
			'attachmentCount'=>$attachmentCount, // 附件的数量 为0表示没有附件
			'attachs'=>$attachs, // 二维数组
		];
	}

	/**
	 * @desc 一个附件下载
	 * @param string $folder 邮箱文件夹名称
	 * @param mixed $uid 邮件的唯一标识
	 * @param mixed $mimeId  一封邮件中某一个区域的惟一值
	 */
	public function downloadAttachment(string $folder,$uid,$mimeId){
		$rcube_message = new rcube_message($uid,$folder,true); // 
		$attachments = $rcube_message->attachments; // 获取二维数组 附件的数量		
		$filename = '';// 初始化附件下载的名称
		if($attachmentCount = count($attachments)){
			$parts = $rcube_message->mime_parts(); // 返回邮件
			// 遍历获得 rcube_message_part 对象
			foreach ($parts as $k => $part) {		    	
		    	if($rcube_message->is_attachment($part) && $part->mime_id == $mimeId) {
		    		$filename = $part->filename;
		    		$attachBody = $rcube_message->get_part_body($mimeId); // 下载某一个附件	
		    	}    
			}
		}else{
			// 异常
			throw new Exception("异常，该邮件不存在附件", 1);			
		}
		if(!$filename){
			throw new Exception("异常，邮件的该附件不存在 mime_id = {$mimeId}", 2);
		}
		$this->downloadFile($filename);
        exit($attachBody);
	}

	/**
	 * 打包下载所有的附件 通过将所有的附件压缩 zip包的形式下载
	 * 下载的压缩包名称就是 标题.zip
	 */
	/*
	public function downloadAllAttachment(string $folder,$uid){
		$rcube_message = new rcube_message($uid,$folder,true);
		$subject = $rcube_message->subject;
		$zipDir= RCUBE_ROOT_PATH .'/attach/'; // 保存附件的目录
		if(!is_dir($zipDir)){
			mkdir($zipDir,0777,true);
		}
		$zipPath = $zipDir .$subject.'.zip';
		if(!is_file($zipPath)){
			fopen($zipPath, "w");
		}
		$attachments = $rcube_message->attachments;
	}
	*/
	/**
	 * 修改邮件状态 已读/未读  星标等
	 * @param mixed $uids 可以是数组 或按英文,分割的字符串
	 * RECENT, ANSWERED, DRAFT, MDNSENT
	 * @param string枚举型 $flag 标记邮件状态
	 * (SEEN:变成已读 | UNSEEN:已读 | FLAGGED:加星标 | UNFLAGGED:取消星标 | DELETED:已删除 | UNDELETED:未删除 | DRAFT:草稿)
	 */
	public function setFlag($uids,string $flag='SEEN',string $folder='',bool $clearCache=true){
		return $this->rcube_imap->set_flag($uids,  $flag,  $folder,  $clearCache);
	}

	/**
	 * 取消标记
	 * SEEN:变成未读 FLAGGED:取消星标
	 */
	public function unsetFlag($uids, string $flag,string $folder=''){
		return $this->rcube_imap->unset_flag( $uids,  $flag,  $folder);
	}

	/**
	 * 文件下载
	 * @param string $filename 文件名称
	 */
	private function downloadFile(string $filename){
		ob_get_clean();
		header('Content-Type:application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
        header('Content-Disposition:attachment ; filename="'. iconv('utf-8','gbk',$filename) .'"');
        header('Cache-Control:max-age=0');
	}

}